package com.tsd.core.utils;

import cn.hutool.extra.pinyin.PinyinUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.common.BitMatrix;
import com.tsd.core.vo.HlpException;
import com.tsd.core.vo.OrderByVo;
import com.tsd.core.vo.WeekInfo;
import org.apache.poi.ss.formula.functions.T;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.net.URLDecoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Hillpool Java项目总工具类
 *
 * @author Hillpool
 */
public class HlpUtils {

    public static final String Format_Yyyy_MM_dd = "yyyy-MM-dd";
    public static final String Format_Yyyy_MM_dd_Cn = "yyyy年MM月dd日";
    public static final String Format_Yyyy_M_d_Cn = "yyyy年M月d日";
    public static final String Format_Yyyy_MM_dd_HH_mm = "yyyy-MM-dd HH:mm";
    public static final String Format_Yyyy_MM_dd_HH_mm_ss_FileName = "yyyy-MM-dd_HH.mm.ss";
    public static final String Format_Yyyy_MM_dd_HH_mm_ss = "yyyy-MM-dd HH:mm:ss";
    public static final String Format_YyyyMMdd = "yyyyMMdd";
    public static final String Format_YyyyMM = "yyyyMM";
    public static final String Format_Yyyy_MM = "yyyy-MM";
    public static final String Format_YyyyMMddHHmmss = "yyyyMMddHHmmss";
    public static final String Format_YyyyMMddHHmmssSSS = "yyyyMMddHHmmssSSS";
    public static final String Format_Yyyy_M_d = "yyyy-M-d";
    public static final String Format_Yyyy_MM_Cn = "yyyy年MM月";
    public static final String Format_Yyyy_M_d_2 = "yyyy/M/d";
    public static final String Format_HH_mm = "HH:mm";
    public static final String Format_Yyyy = "yyyy";
    public static final String Format_Yy = "yy";
    public static final String Format_YyMMdd = "yyMMdd";
    public static final String Format_YyMMddHHmmss = "yyMMddHHmmss";
    public static final String Format_HHmmSS = "HHmmss";
    public static final String Format_YyMM = "yyMM";
    public static final String MD5 = "md5";

    static public DecimalFormat decimalFormat_00 = new DecimalFormat("#0.00");
    static public DecimalFormat decimalFormatInt = new DecimalFormat("#0");

    private static final Pattern CAMEL_PATTERN = Pattern.compile("([A-Za-z\\d]+)(_)?");
    private static final Pattern UNDERLINE_PATTERN = Pattern.compile("[A-Z]([a-z\\d]+)?");

    /**
     * 按指定格式将日期转为字符串
     *
     * @param date
     * @param format
     * @return
     */
    public static String date2String(Date date, String format) {
        SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.CHINESE);
        return sdf.format(date);
    }

    /**
     * 按指定格式将日期转为字符串
     *
     * @param date
     * @param format
     * @return
     */
    public static String localDateTime2String(LocalDateTime date, String format) {
        DateTimeFormatter sdf = DateTimeFormatter.ofPattern(format);
        return sdf.format(date);
    }

    /**
     * 按指定格式将字符串转为日期(按照中国时区)
     *
     * @param date
     * @param format
     * @return
     */
    public static Date string2Date(String date, String format) throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.CHINESE);
        return sdf.parse(date);
    }

    /**
     * 清理Date值，如果发现date的getTime==0，则设置为null
     * 使用场景是某些原系统的数据是1970-01-01 00:00:00的数据
     *
     * @param date
     * @return
     */
    static public Date cleanDate(Date date) {
        if (date != null && date.getTime() == 0) {
            date = null;
        }
        return date;
    }

    /**
     * 提取字符串中的url，截取原则：a）http://开头 b）空白或者非中文结尾
     *
     * @param s
     * @return
     */
    static public List<String> getUrl(String s) {
        Pattern pattern = Pattern
                .compile("http://[\\S\\.]+[:\\d]?[/\\S]+\\??[\\S=\\S&?]+[^\u4e00-\u9fa5]");
        Matcher matcher = pattern.matcher(s);
        List<String> list = new ArrayList<String>();
        while (matcher.find()) {
            list.add(matcher.group());
        }
        return list;
    }


    /**
     * 判断文件是否存在
     *
     * @param fileName
     * @return
     */
    static public boolean fileExists(String fileName) {
        File f = new File(fileName);
        return f.exists();
    }

    /**
     * 对象是否为空，对于字符串而言，则有3种取值认为是空
     *
     * @param s
     * @return
     */
    static public boolean isEmpty(Object s) {
        if (s instanceof String) {
            String ss = (String) s;
            return ss.trim().isEmpty() || "null".equalsIgnoreCase(ss);
        } else {
            return s == null;
        }
    }

    /**
     * 列表是否为空
     *
     * @param list
     * @return
     */
    public static <T> boolean isEmptyList(List<T> list) {
        return list == null || list.isEmpty();
    }

    /**
     * 判断字符串是否json字符串数组
     *
     * @param json
     * @return
     */
    public static boolean isArrayJson(String json) {
        return !isEmpty(json) && json.startsWith("[") && json.endsWith("]");
    }

    /**
     * 判断字符串是否对象字符串
     *
     * @param json
     * @return
     */
    public static boolean isObjectJson(String json) {
        return !isEmpty(json) && json.startsWith("{") && json.endsWith("}");
    }

    /**
     * 创建指定大小的二维码图片保存到指定的文件中
     *
     * @param value    二维码值
     * @param fileName 完整的文件路径
     * @param width    宽度
     * @param height   高度
     */
    static public boolean create2DBarcode(String value, String fileName, int width, int height) {
        //二维码的图片格式
        String format = "jpg";
        HashMap<EncodeHintType, String> hints = new HashMap<EncodeHintType, String>();
        //内容所使用编码
        hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
        BitMatrix bitMatrix;
        try {
            bitMatrix = new MultiFormatWriter().encode(value,
                    BarcodeFormat.QR_CODE, width, height, hints);
            //生成二维码
            File outputFile = new File(fileName);
            if (!outputFile.getParentFile().exists()) {
                outputFile.getParentFile().mkdirs();
            }
            MatrixToImageWriter.writeToFile(bitMatrix, format,
                    outputFile);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 创建二维码图片（大小为300*300）
     *
     * @param value    二维码值
     * @param fileName 保存图片的文件完整路径
     */
    static public boolean create2DBarcode(String value, String fileName) {
        int width = 300;
        int height = 300;
        return create2DBarcode(value, fileName, width, height);
    }

    /**
     * 根据指定的字符串生成指定长度的字符串，不够的前面补充指定的字符
     *
     * @param s
     * @param len
     * @return
     */
    public static String getLenStrWithPreChar(String s, int len, String c) {
        if (HlpUtils.isEmpty(s)) {
            s = "";
        }
        if (s.length() == len) {
            return s;
        } else if (s.length() > len) {
            return s.substring(s.length() - len);
        }
        StringBuffer buf = new StringBuffer();
        int times = (len - s.length()) / c.length() + 1;
        for (int i = 0; i < times; i++) {
            buf.append(c);
        }
        buf.append(s);
        return buf.substring(buf.length() - len);
    }

    /**
     * 根据指定的字符串生成指定长度的字符串，不够的后面补充指定的字符
     *
     * @param s
     * @param len
     * @return
     */
    public static String getLenStrWithSuffixChar(String s, int len, String c) {
        if (HlpUtils.isEmpty(s)) {
            s = "";
        }
        if (s.length() == len) {
            return s;
        } else if (s.length() > len) {
            return s.substring(0, len);
        }
        StringBuffer buf = new StringBuffer();
        int times = (len - s.length()) / c.length() + 1;
        for (int i = 0; i < times; i++) {
            buf.append(c);
        }
        return buf.substring(0, len);
    }

    /**
     * 获取与指定日期距离为相对天数（可正可负）的日期
     *
     * @param date
     * @param days
     * @return
     */
    static public Date getNextDate(Date date, int days) {
        if (date == null) {
            return null;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DAY_OF_YEAR, days);
        return calendar.getTime();
    }

    /**
     * 取与指定时间距离为相对分钟（可正可负）的时间
     *
     * @param date
     * @param mins
     * @return
     */
    private static Date getNextMinute(Date date, int mins) {
        if (date == null) {
            return null;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.MINUTE, mins);
        return calendar.getTime();
    }

    public static Date getNextDateByMinute(Date date, int mins) {
        if (date == null) {
            return null;
        } else {
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(date);
            calendar.add(12, mins);
            return calendar.getTime();
        }
    }

    public static Date getNextDateByms(Date date, long ms) {
        if (date == null) {
            return null;
        } else {
            long next = date.getTime() + ms;
            return new Date(next);
        }
    }

    public static Date getNextDateByHour(Date date, int hours) {
        if (date == null) {
            return null;
        } else {
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(date);
            calendar.add(10, hours);
            return calendar.getTime();
        }
    }

    /**
     * 去除-号的uuid值
     *
     * @return
     */
    public static String getUUIDWithout_() {
        return UUID.randomUUID().toString().replace("-", "");
    }

    /**
     * 生成主键（不含-号）
     *
     * @return
     */
    public static String getPrimaryKey() {
        return getUUIDWithout_();
    }

    static final private String MD5_SALT = "23456787454534543534fgdfgdLKoiddd999";
    static final private String MD5_ALGORITHM = "md5";

    /**
     * 对密码进行Md5加密
     *
     * @param password
     * @return 16进制的密文字符串
     * @throws NoSuchAlgorithmException
     */
    public static String doMd5(String password) throws NoSuchAlgorithmException {
        //为了提高加密强度，密码是在原来明文的前面加上随机字母
        return doMD(MD5_SALT + password, MD5_ALGORITHM);
    }

    /**
     * 对口令进行加密
     *
     * @param password
     * @param algorithm
     * @return
     * @throws NoSuchAlgorithmException
     */
    public static String doMD(String password, String algorithm)
            throws NoSuchAlgorithmException {
        MessageDigest md = MessageDigest.getInstance(algorithm);
        md.update(password.getBytes());
        byte[] result = md.digest();
        return byteToHexString(result);
    }

    /**
     * 数组转为16进制字符串
     *
     * @param b
     * @return
     */
    private static String byteToHexString(byte[] b) {
        StringBuffer hexString = new StringBuffer();
        for (int i = 0; i < b.length; i++) {
            String hex = Integer.toHexString(b[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            hexString.append(hex.toUpperCase());
        }
        return hexString.toString();
    }

    /**
     * 获取类所在的路径(对于中文等路径需要进行url decode)
     *
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String getClassPath() throws UnsupportedEncodingException {
        String s = Objects.requireNonNull(HlpUtils.class.getResource("/")).getPath();
        s = URLDecoder.decode(s, "UTF-8");
        return s;
    }

    /**
     * 获取web app所在的路径 .../appName
     * appName\WEB-INF\classes
     *
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String getWebAppPath() throws Exception {
        String s = getClassPath();
        File file = new File(s);
        file = file.getParentFile();
        if (file != null) {
            file = file.getParentFile();
        }
        if (file == null) {
            throw new HlpException("无法获取Web App所在目录");
        }
        return file.getAbsolutePath();
    }

    /**
     * 从map中获取字符串值
     *
     * @param map
     * @param key
     * @return
     */
    public static String getString(Map<String, Object> map, String key) {
        if (map == null){
            return null;
        }
        Object o = map.get(key);
        if (o != null) {
            if (o instanceof Integer) {
                return String.valueOf(o);
            } else if (o instanceof String) {
                return (String) o;
            } else if (o instanceof Double) {
                return String.valueOf(o);
            } else if (o instanceof BigDecimal) {
                return String.valueOf(o);
            } else if (o instanceof Long) {
                return String.valueOf(o);
            } else if (o instanceof Map) {
                return JSON.toJSONString(o);
            } else {
                return o.toString();
            }
        }
        return null;
    }

    /**
     * 从Map中获取key对应的值，如果没有，则使用所指定的默认值
     *
     * @param map
     * @param key
     * @param defaultValue
     * @return
     */
    public static String getString(Map<String, Object> map, String key, String defaultValue) {
        String val = getString(map, key);
        return HlpUtils.isEmpty(val) ? defaultValue : val;
    }

    /**
     * 从Map中获取值，如果为空，则报错
     *
     * @param map
     * @param key
     * @param must
     * @param nullExceptionString 为空时的提示语
     * @return
     * @throws HlpException
     */
    public static String getString(Map<String, Object> map, String key, boolean must, String nullExceptionString) throws HlpException {
        String val = getString(map, key);
        if (must) {
            if (HlpUtils.isEmpty(val)) {
                throw new HlpException(nullExceptionString);
            }
        }
        return val;
    }

    /**
     * 从map里获取整数
     *
     * @param map
     * @param key
     * @return
     * @throws HlpException
     */
    public static Integer getInteger(Map<String, Object> map, String key) throws HlpException {
        if (map == null) {
            return null;
        }
        Object o = map.get(key);
        if (o == null) {
            return null;
        }
        try {
            if (o instanceof Integer) {
                return (Integer) o;
            } else if (o instanceof String) {
                if (HlpUtils.isEmpty(o)) {
                    return null;
                }
                return Integer.parseInt((String) o);
            } else if (o instanceof Long) {
                return ((Long) o).intValue();
            } else if (o instanceof BigDecimal) {
                return ((BigDecimal) o).intValue();
            } else if (o instanceof Double) {
                return ((Double) o).intValue();
            } else {
                throw new HlpException("无法转换数据");
            }
        } catch (NumberFormatException e) {
            throw new HlpException("数字格式不对：" + key);
        }
    }

    /**
     * 从Map中获取整数，如果为空，则使用指定的默认值
     *
     * @param map
     * @param key
     * @param defaultValue
     * @return
     * @throws HlpException
     */
    public static Integer getInteger(Map<String, Object> map, String key, Integer defaultValue) throws HlpException {
        Integer val = getInteger(map, key);
        return HlpUtils.isEmpty(val) ? defaultValue : val;
    }

    /**
     * 从map里获取Double
     *
     * @param map
     * @param key
     * @return
     * @throws HlpException
     */
    public static Double getDouble(Map<String, Object> map, String key) throws HlpException {
        if (map == null) {
            return null;
        }
        Object o = map.get(key);
        if (o == null) {
            return null;
        }
        try {
            if (o instanceof Integer) {
                return ((Integer) o).doubleValue();
            } else if (o instanceof String) {
                if (HlpUtils.isEmpty(o)) {
                    return null;
                }
                return Double.parseDouble((String) o);
            } else if (o instanceof Double) {
                return (Double) o;
            } else if (o instanceof BigDecimal) {
                return ((BigDecimal) o).doubleValue();
            } else if (o instanceof Long) {
                return ((Long) o).doubleValue();
            } else {
                throw new HlpException("无法转换数据");
            }
        } catch (NumberFormatException e) {
            throw new HlpException("数字格式不对：" + key);
        }
    }

    /**
     * 从map里获取Double，如果为空，则取指定的默认值
     *
     * @param map
     * @param key
     * @param defaultValue
     * @return
     * @throws HlpException
     */
    public static double getDouble(Map<String, Object> map, String key, Double defaultValue) throws HlpException {
        Double val = getDouble(map, key);
        return HlpUtils.isEmpty(val) ? defaultValue : val;
    }

    /**
     * 从map里获取Long
     *
     * @param map
     * @param key
     * @return
     * @throws HlpException
     */
    public static Long getLong(Map<String, Object> map, String key) throws HlpException {
        if (map == null) {
            return null;
        }
        Object o = map.get(key);
        if (o == null) {
            return null;
        }
        try {
            if (o instanceof Integer) {
                return ((Integer) o).longValue();
            } else if (o instanceof String) {
                if (HlpUtils.isEmpty(o)) {
                    return null;
                }
                return Long.parseLong((String) o);
            } else if (o instanceof Double) {
                return ((Double) o).longValue();
            } else if (o instanceof BigDecimal) {
                return ((BigDecimal) o).longValue();
            } else if (o instanceof Long) {
                return (Long) o;
            } else {
                throw new HlpException("无法转换数据");
            }
        } catch (NumberFormatException e) {
            throw new HlpException("数字格式不对：" + key);
        }
    }

    /**
     * 若干个参数中是否有一个为空
     *
     * @param list
     * @return
     */
    public static boolean isEmptyOne(Object... list) {
        boolean isEmpty = false;
        for (Object s : list) {
            isEmpty = isEmpty(s);
            if (isEmpty) {
                break;
            }
        }
        return isEmpty;
    }

    /**
     * 若干个参数中是否有一个不为空
     *
     * @param list
     * @return
     */
    public static boolean isNotEmptyOne(Object... list) {
        boolean isNotEmpty = false;
        for (Object s : list) {
            isNotEmpty = !isEmpty(s);
            if (isNotEmpty) {
                break;
            }
        }
        return isNotEmpty;
    }

    /**
     * 若干个参数中是否所有都不为空
     *
     * @param list
     * @return
     */
    public static boolean isAllNotEmpty(Object... list) {
        boolean isAllNotEmpty = true;
        for (Object s : list) {
            isAllNotEmpty = !isEmpty(s);
            if (!isAllNotEmpty) {
                break;
            }
        }
        return isAllNotEmpty;
    }

    public static void main(String[] args) {
        Date d = new Date();
        d = getNextMinute(d, 10);
        System.err.println(d);
    }

    /**
     * 复制参数（从源map到目标map，可以不同的key名称）
     *
     * @param map
     * @param params
     * @param srcDestKey
     * @param must    是否必须，如果是，如果没有的话，则报错
     * @param like    是否模糊查询,如果是，则自动在值前后加上%
     * @throws HlpException
     */
    public static void copyStringValue(Map<String, Object> map, Map<String, Object> params, String srcDestKey, boolean must, boolean like) throws HlpException {
        copyStringValue(map, params, srcDestKey, srcDestKey, must, like);
    }

    /**
     * 复制参数（从源map到目标map，可以不同的key名称）
     *
     * @param map
     * @param params
     * @param srcKey
     * @param destKey
     * @param must    是否必须，如果是，如果没有的话，则报错
     * @param like    是否模糊查询,如果是，则自动在值前后加上%
     * @throws HlpException
     */
    public static void copyStringValue(Map<String, Object> map, Map<String, Object> params, String srcKey, String destKey, boolean must, boolean like) throws HlpException {
        if (params == null) {
            throw new HlpException("目标为空");
        }
        if (must) {
            if (map == null) {
                throw new HlpException("源参数为空");
            }
            if (!map.containsKey(srcKey)) {
                throw new HlpException("源参数" + srcKey + "不存在");
            }
        }
        String val = getString(map, srcKey);
        if (must && HlpUtils.isEmpty(val)) {
            throw new HlpException("源参数" + srcKey + "不存在或者为空");
        }
        if (!HlpUtils.isEmpty(val)) {
            if (like) {
                val = "%" + val + "%";
            }
            params.put(destKey, val);
        }
    }

    /**
     * 复制参数（从源map到目标map，可以不同的key名称）
     *
     * @param map
     * @param params
     * @param must   是否必须，如果是，如果没有的话，则报错
     * @param like   是否模糊查询,如果是，则自动在值前后加上%
     * @param keys
     * @throws HlpException
     */
    public static void copyStringValues(Map<String, Object> map, Map<String, Object> params, boolean must, boolean like, String... keys) throws HlpException {
        for (String key : keys) {
            copyStringValue(map, params, key, key, must, like);
        }
    }

    /**
     * 复制参数（从源map到目标map，可以不同的key名称）
     *
     * @param map
     * @param params
     * @param srcKey
     * @param destKey
     * @param must      是否必须，如果是，如果没有的话，则报错
     * @param like      是否模糊查询,如果是，则自动在值前后加上%
     * @param startWith 是否只查询以此值为开头
     * @throws HlpException
     */
    public static void copyStringValue(Map<String, Object> map, Map<String, Object> params, String srcKey, String destKey, boolean must, boolean like, boolean startWith) throws HlpException {
        if (params == null) {
            throw new HlpException("目标为空");
        }
        if (must) {
            if (map == null) {
                throw new HlpException("源参数为空");
            }
            if (!map.containsKey(srcKey)) {
                throw new HlpException("源参数" + srcKey + "不存在");
            }
        }
        String val = getString(map, srcKey);
        if (must && HlpUtils.isEmpty(val)) {
            throw new HlpException("源参数" + srcKey + "不存在或者为空");
        }
        if (!HlpUtils.isEmpty(val)) {
            if (like) {
                val = "%" + val + "%";
            } else {
                if (startWith) {
                    val = val + "%";
                }
            }
            params.put(destKey, val);
        }
    }

    /**
     * 复制参数（从源map到目标map，可以不同的key名称）
     *
     * @param map
     * @param params
     * @param srcKey
     * @param destKey
     * @param must    是否必须，如果是，如果没有的话，则报错
     * @throws HlpException
     */
    public static void copyIntegerValue(Map<String, Object> map, Map<String, Object> params, String srcKey, String destKey, boolean must) throws HlpException {
        if (params == null) {
            throw new HlpException("目标为空");
        }
        if (must) {
            if (map == null) {
                throw new HlpException("源参数为空");
            }
            if (!map.containsKey(srcKey)) {
                throw new HlpException("源参数" + srcKey + "不存在");
            }
        }
        Integer val = getInteger(map, srcKey);
        if (must && HlpUtils.isEmpty(val)) {
            throw new HlpException("源参数" + srcKey + "不存在或者为空");
        }
        if (!HlpUtils.isEmpty(val)) {
            params.put(destKey, val);
        }
    }

    /**
     * 复制参数（从源map到目标map，可以不同的key名称）
     *
     * @param map
     * @param params
     * @param srcDestKey
     * @param must
     * @param defaultValue 如果源map中没有，则取默认值加入目标map中
     * @throws HlpException
     */
    public static void copyIntegerValue(Map<String, Object> map, Map<String, Object> params, String srcDestKey, boolean must, Integer defaultValue) throws HlpException {
        copyIntegerValue(map, params, srcDestKey, srcDestKey, must, defaultValue);
    }

    /**
     * 复制参数（从源map到目标map，可以不同的key名称）
     *
     * @param map
     * @param params
     * @param srcKey
     * @param destKey
     * @param must
     * @param defaultValue 如果源map中没有，则取默认值加入目标map中
     * @throws HlpException
     */
    public static void copyIntegerValue(Map<String, Object> map, Map<String, Object> params, String srcKey, String destKey, boolean must, Integer defaultValue) throws HlpException {
        if (params == null) {
            throw new HlpException("目标为空");
        }
        if (must && HlpUtils.isEmpty(defaultValue)) {
            if (map == null) {
                throw new HlpException("源参数为空");
            }
            if (!map.containsKey(srcKey) && defaultValue == null) {
                throw new HlpException("源参数" + srcKey + "不存在");
            }
        }
        Integer val = getInteger(map, srcKey);
        if (must && HlpUtils.isEmpty(val)) {
            if (defaultValue != null) {
                val = defaultValue;
            } else {
                throw new HlpException("默认值为空");
            }
        }
        if (!HlpUtils.isEmpty(val)) {
            params.put(destKey, val);
        }
    }

    /**
     * 复制参数（从源map到目标map，可以不同的key名称）
     *
     * @param map
     * @param params
     * @param srcKey
     * @param destKey
     * @param must    是否必须，如果是，如果没有的话，则报错
     * @throws HlpException
     */
    public static void copyLongValue(Map<String, Object> map, Map<String, Object> params, String srcKey, String destKey, boolean must) throws HlpException {
        if (params == null) {
            throw new HlpException("目标为空");
        }
        if (must) {
            if (map == null) {
                throw new HlpException("源参数为空");
            }
            if (!map.containsKey(srcKey)) {
                throw new HlpException("源参数" + srcKey + "不存在");
            }
        }
        Long val = getLong(map, srcKey);
        if (must && HlpUtils.isEmpty(val)) {
            throw new HlpException("源参数" + srcKey + "不存在或者为空");
        }
        if (!HlpUtils.isEmpty(val)) {
            params.put(destKey, val);
        }
    }

    /**
     * 复制参数（从源map到目标map，可以不同的key名称）
     *
     * @param map
     * @param params
     * @param srcKey
     * @param destKey
     * @param must    是否必须，如果是，如果没有的话，则报错
     * @throws HlpException
     */
    public static void copyDoubleValue(Map<String, Object> map, Map<String, Object> params, String srcKey, String destKey, boolean must) throws HlpException {
        if (params == null) {
            throw new HlpException("目标为空");
        }
        if (must) {
            if (map == null) {
                throw new HlpException("源参数为空");
            }
            if (!map.containsKey(srcKey)) {
                throw new HlpException("源参数" + srcKey + "不存在");
            }
        }
        Double val = getDouble(map, srcKey);
        if (must && HlpUtils.isEmpty(val)) {
            throw new HlpException("源参数" + srcKey + "不存在或者为空");
        }
        if (!HlpUtils.isEmpty(val)) {
            params.put(destKey, val);
        }
    }

    /**
     * 复制参数（从源map到目标map，可以不同的key名称）
     *
     * @param map
     * @param params
     * @param srcKey
     * @param destKey
     * @param must    是否必须，如果是，如果没有的话，则报错
     * @param format  日期格式
     * @throws HlpException
     */
    public static void copyDateValue(Map<String, Object> map, Map<String, Object> params, String srcKey, String destKey, boolean must, String format) throws HlpException, ParseException {
        if (params == null) {
            throw new HlpException("目标为空");
        }
        if (must) {
            if (map == null) {
                throw new HlpException("源参数为空");
            }
            if (!map.containsKey(srcKey)) {
                throw new HlpException("源参数" + srcKey + "不存在");
            }
        }
        String dateStr = getString(map, srcKey);
        if (must && HlpUtils.isEmpty(dateStr)) {
            throw new HlpException("源参数" + srcKey + "不存在或者为空");
        }
        if (!HlpUtils.isEmpty(dateStr)) {
            params.put(destKey, string2Date(dateStr, format));
        }
    }

    /**
     * 复制参数（从源map到目标map，可以不同的key名称）
     *
     * @param map
     * @param params
     * @param srcDestKey
     * @param must    是否必须，如果是，如果没有的话，则报错
     * @param clazz
     * @throws HlpException
     */
    public static <T> void copyListValue(Map<String, Object> map, Map<String, Object> params, String srcDestKey, boolean must, Class<T> clazz) throws HlpException {
        copyListValue(map, params, srcDestKey, srcDestKey, must, clazz);
    }

    /**
     * 复制参数（从源map到目标map，可以不同的key名称）
     *
     * @param map
     * @param params
     * @param srcKey
     * @param destKey
     * @param must    是否必须，如果是，如果没有的话，则报错
     * @param clazz
     * @throws HlpException
     */
    public static <T> void copyListValue(Map<String, Object> map, Map<String, Object> params, String srcKey, String destKey, boolean must, Class<T> clazz) throws HlpException {
        if (params == null) {
            throw new HlpException("目标为空");
        }
        if (must) {
            if (map == null) {
                throw new HlpException("源参数为空");
            }
            if (!map.containsKey(srcKey)) {
                throw new HlpException("源参数" + srcKey + "不存在");
            }
        }
        Object obj = map.get(srcKey);
        List<T> list = JSONArray.parseArray(JSON.toJSONString(obj), clazz);
        if (must && HlpUtils.isEmptyList(list)) {
            throw new HlpException("源参数" + srcKey + "不存在或者为空");
        }
        if (!HlpUtils.isEmptyList(list)) {
            params.put(destKey, list);
        }
    }

    /**
     * 处理日期范围参数（String）
     * 例如：srcKey=the_date, xml中语句应该是
     * the_date >= #{the_date_begin,jdbcType=VARCHAR} and the_date < #{the_date_end,jdbcType=VARCHAR}
     * 或the_date >= #{the_date_begin,jdbcType=DATE} and the_date < #{the_date_end,jdbcType=DATE}
     *
     * @param map    源mpa
     * @param params 目标map
     * @param srcDestKey key值
     * @param must   是否必须，如果是，如果没有的话，则报错
     * @throws HlpException
     */
    public static void copyDateRangeValue(Map<String, Object> map, Map<String, Object> params, String srcDestKey, boolean must) throws Exception {
        copyDateRangeValue(map, params, srcDestKey, srcDestKey, must);
    }


    /**
     * 处理日期范围参数（String）
     * 例如：srcKey=the_date, xml中语句应该是
     * the_date >= #{the_date_begin,jdbcType=VARCHAR} and the_date < #{the_date_end,jdbcType=VARCHAR}
     * 或the_date >= #{the_date_begin,jdbcType=DATE} and the_date < #{the_date_end,jdbcType=DATE}
     *
     * @param map    源mpa
     * @param params 目标map
     * @param srcKey 源key值
     * @param srcKey 目标key值
     * @param must   是否必须，如果是，如果没有的话，则报错
     * @throws HlpException
     */
    public static void copyDateRangeValue(Map<String, Object> map, Map<String, Object> params, String srcKey, String destKey, boolean must) throws Exception {
        if (params == null) {
            throw new HlpException("目标为空");
        }
        if (must) {
            if (map == null) {
                throw new HlpException("源参数为空");
            }
            if (!map.containsKey(srcKey)) {
                throw new HlpException("源参数" + srcKey + "不存在");
            }
        }
        String val = getString(map, srcKey);
        if (must && HlpUtils.isEmpty(val)) {
            throw new HlpException("源参数" + srcKey + "不存在或者为空");
        }
        if (!HlpUtils.isEmpty(val)) {
            if (JSONUtil.isJsonArray(val)) {
                List<String> valueList = JSON.parseArray(val, String.class);
                if (!HlpUtils.isEmpty(valueList) && valueList.size() == 2) {
                    Date date_end = HlpUtils.string2Date(valueList.get(1), "yyyy-MM-dd");
                    date_end = HlpUtils.getNextDate(date_end, 1);
                    params.put(destKey + "_begin", valueList.get(0));
                    params.put(destKey + "_end", HlpUtils.date2String(date_end, "yyyy-MM-dd"));
                }
            } else {
                params.put(srcKey, val);
            }
        }
    }

    /**
     * 判断当前时间是否在sourceTime区间
     *
     * @return
     * @author zsy
     */
    public static boolean isInTime(String sourceTime, String curTime) {
        if (sourceTime == null || !sourceTime.contains("-") || !sourceTime.contains(":")) {
            throw new IllegalArgumentException("Illegal Argument arg:" + sourceTime);
        }
        if (curTime == null || !curTime.contains(":")) {
            throw new IllegalArgumentException("Illegal Argument arg:" + curTime);
        }
        String[] args = sourceTime.split("-");
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");
        try {
            long now = sdf.parse(curTime).getTime();
            long start = sdf.parse(args[0]).getTime();
            long end = sdf.parse(args[1]).getTime();
            if (args[1].equals("00:00")) {
                args[1] = "24:00";
            }
            if (end < start) {
                return now < end || now >= start;
            } else {
                return now >= start && now < end;
            }
        } catch (ParseException e) {
            e.printStackTrace();
            throw new IllegalArgumentException("Illegal Argument arg:" + sourceTime);
        }
    }

    /**
     * 获取一个月的最后一天
     *
     * @param date
     * @return
     */
    public static Integer getMaxDay4Month(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.DATE, 1);
        return calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
    }

    /**
     * 2个对象是否相同
     *
     * @param obj1
     * @param obj2
     * @return
     */
    public static boolean isEqual(Object obj1, Object obj2) {
        if (HlpUtils.isEmpty(obj1) && HlpUtils.isEmpty(obj2)) {
            return true;
        } else if (HlpUtils.isEmpty(obj1) && !HlpUtils.isEmpty(obj2)) {
            return false;
        } else if (!HlpUtils.isEmpty(obj1) && HlpUtils.isEmpty(obj2)) {
            return false;
        } else {
            return obj1.equals(obj2);
        }
    }

    /**
     * 字符串去除前后空格
     *
     * @param val
     * @return
     */
    public static String trim(String val) {
        if (val != null) {
            val = val.trim();
        }
        return val;
    }

    /**
     * 复制Map，如果有才复制
     *
     * @param dest
     * @param src
     * @param key
     */
    public static void copyMap(Map<String, Object> dest, Map<String, Object> src, String key) {
        if (src.containsKey(key) && !HlpUtils.isEmpty(src.get(key))) {
            dest.put(key, src.get(key));
        }
    }

    /**
     * 在指定日期的若干月份（可正可负）的日期
     *
     * @param date
     * @param months
     * @return
     */
    public static Date getNextDateByMonth(Date date, int months) {
        if (date == null) {
            return null;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.MONTH, months);
        return calendar.getTime();
    }

    public static <T> List<T> convert(List<?> list, Class<T> c) {
        return (List<T>) list;
    }

    /**
     * 将列表转为map，以指定的属性值作为key值
     *
     * @param list
     * @param key
     * @return
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     */
    public static Map<Object, Object> list2Map(List list, String key) throws NoSuchFieldException, IllegalAccessException {
        Map<Object, Object> map = new HashMap<>();
        if (list != null && list.size() > 0) {
            //获取这个类的所有属性
            List<Field> fieldList = new ArrayList<>();
            Class<?> tempClass = list.get(0).getClass();
            while (tempClass != null) {
                fieldList.addAll(Arrays.asList(tempClass.getDeclaredFields()));
                tempClass = tempClass.getSuperclass();
            }
            //得到所有属性
            Field field = ListUtil.get(fieldList, t -> key.equalsIgnoreCase(t.getName()));
            if (field != null) {
                //打开私有访问
                field.setAccessible(true);
                for (Object obj : list) {
                    //获取值
                    Object value = field.get(obj);
                    map.put(value, obj);
                }
            }
        }
        return map;
    }

    /**
     * 获取日期部分
     *
     * @param date
     * @return
     * @throws ParseException
     */
    public static Date getTheDate(Date date) throws ParseException {
        if (date != null) {
            return HlpUtils.string2Date(HlpUtils.date2String(date, "yyyy-MM-dd"), "yyyy-MM-dd");
        }
        return null;
    }


    /**
     * 获取必填的字符串参数
     *
     * @param map
     * @param key
     * @param emptyTip
     * @return
     */
    public static String getStringMust(Map<String, Object> map, String key, String emptyTip) throws HlpException {
        String val = getString(map, key);
        if (HlpUtils.isEmpty(val)) {
            throw new HlpException(emptyTip);
        }
        return val;
    }

    public static void generateSort(Map<String, Object> map, Map<String, Object> params) {
    }

    /**
     * 下划线转驼峰法(默认小驼峰)
     *
     * @param line       源字符串
     * @param smallCamel 大小驼峰,是否为小驼峰(驼峰，第一个字符是大写还是小写)
     * @return 转换后的字符串
     */
    public static String underline2Camel(String line, boolean... smallCamel) {
        if (line == null || "".equals(line)) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        Matcher matcher = CAMEL_PATTERN.matcher(line);
        //匹配正则表达式
        while (matcher.find()) {
            String word = matcher.group();
            //当是true 或则是空的情况
            if ((smallCamel.length == 0 || smallCamel[0]) && matcher.start() == 0) {
                sb.append(Character.toLowerCase(word.charAt(0)));
            } else {
                sb.append(Character.toUpperCase(word.charAt(0)));
            }
            int index = word.lastIndexOf('_');
            if (index > 0) {
                sb.append(word.substring(1, index).toLowerCase());
            } else {
                sb.append(word.substring(1).toLowerCase());
            }
        }
        return sb.toString();
    }

    /**
     * 驼峰法转下划线(默认小写)
     *
     * @param line    源字符串
     * @param isLower 大小写,是否为小写
     * @return 转换后的字符串
     */
    public static String camel2Underline(String line, boolean... isLower) {
        if (line == null || "".equals(line)) {
            return "";
        }
        line = String.valueOf(line.charAt(0)).toUpperCase().concat(line.substring(1));
        StringBuilder sb = new StringBuilder();
        Matcher matcher = UNDERLINE_PATTERN.matcher(line);
        while (matcher.find()) {
            String word = matcher.group();
            if (isLower.length == 0 || isLower[0]) {
                sb.append(word.toLowerCase());
            } else {
                sb.append(word.toUpperCase());
            }
            sb.append(matcher.end() == line.length() ? "" : "_");
        }
        return sb.toString();
    }

    /**
     * 获取字符串的右边若干字符
     *
     * @param str
     * @param count
     * @return
     */
    public static String right(String str, int count) {
        int index = str.length() - count;
        if (index < 1) {
            return str;
        } else {
            return str.substring(index);
        }
    }

    /**
     * 根据年份、周次获取所在的月份
     *
     * @param year
     * @param week
     * @return
     */
    public static Integer getMonth(Integer year, Integer week) {
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
        calendar.set(Calendar.YEAR, year);
        calendar.set(Calendar.WEEK_OF_YEAR, week);
        calendar.add(Calendar.DAY_OF_WEEK, 3);
        return calendar.get(Calendar.MONTH) + 1;
    }

    /**
     * 获取周次信息
     *
     * @param date
     * @return
     */
    public static WeekInfo getWeekInfo(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        int week1 = calendar.get(Calendar.WEEK_OF_YEAR);
        int year = calendar.get(Calendar.YEAR);
        int month1 = calendar.get(Calendar.MONTH) + 1;
        int month = getMonth(year, week1);
        if (month1 == 12 && month == 1) {
            //刚好跨年
            year++;
            week1 = 1;
        }
        WeekInfo wi = new WeekInfo();
        wi.setYear(year);
        wi.setMonth(month);
        wi.setWeek(week1);
        return wi;
    }

    /**
     * 根据年份和周次获取第一天或最后一天
     *
     * @param year 年份
     * @param week 周次
     * @param flag 是否最后一天，false-第一天，true-最后一天
     * @return
     */
    public static Date getWeekFirstOrLastDate2(Integer year, Integer week, boolean flag) {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.WEEK_OF_YEAR, week);
        if (flag) {
            cal.set(Calendar.WEEK_OF_YEAR, week + 1);
            cal.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
        } else {
            cal.set(Calendar.WEEK_OF_YEAR, week);
        }
        return cal.getTime();
    }

    /**
     * 获取指定日期所在月份的第一天
     *
     * @param date
     * @return
     */
    public static Date getMonthFirstDate(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.DATE, 1);//把日期设置为当月第一天
        return calendar.getTime();
    }

    /**
     * 获取指定日期所在月份的最后一天
     *
     * @param date
     * @return
     */
    public static Date getMonthLastDate(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.set(Calendar.DATE, 1);//把日期设置为当月第一天
        calendar.roll(Calendar.DATE, -1);//日期回滚一天，也就是最后一天
        return calendar.getTime();
    }

    /**
     * 获取指定日期所在季度的第一天
     *
     * @param date
     * @return
     * @throws Exception
     */
    public static Date getSeasonFirstDate(Date date) throws Exception {
        String yyyy = date2String(date, "yyyy");
        String mm = date2String(date, "MM");
        int month = Integer.parseInt(mm);
        if (month < 4) {
            yyyy = yyyy + "-01-01";
        } else if (month < 7) {
            yyyy = yyyy + "-04-01";
        } else if (month < 10) {
            yyyy = yyyy + "-07-01";
        } else {
            yyyy = yyyy + "-10-01";
        }
        return string2Date(yyyy, Format_Yyyy_MM_dd);
    }

    /**
     * 获取指定日期的当年第一天
     *
     * @param date
     * @return
     * @throws Exception
     */
    public static Date getYearFirstDate(Date date) throws Exception {
        String yyyy = date2String(date, "yyyy");
        yyyy = yyyy + "-01-01";
        return string2Date(yyyy, Format_Yyyy_MM_dd);
    }

    /**
     * 从map获取指定key的值，并转为字符串列表
     *
     * @param map
     * @param key
     * @return
     */
    public static List<String> getStringList(Map<String, Object> map, String key) {
        if (map == null) {
            return null;
        }
        Object o = map.get(key);
        if (o == null) {
            return null;
        }
        String json = JSON.toJSONString(o);
        if (json.startsWith("[")) {
            List<String> list = JSON.parseArray(json, String.class);
            return list;
        } else {
            List<String> list = new ArrayList<>();
            list.add(json);
            return list;
        }
    }

    /**
     * 判断字符串是否为空
     *
     * @param parent_sid
     * @param emptyStrList 认为是空的字符串列表
     * @return
     */
    public static boolean isEmpty(String parent_sid, String... emptyStrList) {
        boolean ret = HlpUtils.isEmpty(parent_sid);
        if (!ret && emptyStrList != null) {
            for (String s : emptyStrList) {
                if (parent_sid.equals(s)) {
                    ret = true;
                    break;
                }
            }
        }
        return ret;
    }

    /**
     * 获取字符串内的数字部份
     *
     * @param code
     * @return
     */
    public static Integer getNumberPart(String code) {
        String regEx = "[^0-9]";
        Pattern p = Pattern.compile(regEx);
        Matcher m = p.matcher(code);
        String ret = m.replaceAll("").trim();
        if (HlpUtils.isEmpty(ret)) {
            return null;
        } else {
            return Integer.parseInt(ret);
        }
    }

    /**
     * 根据条件抛出异常
     *
     * @param value
     * @param msg
     * @throws HlpException
     */
    public static void throwHlpException(boolean value, String msg) throws HlpException {
        if (value) {
            throw new HlpException(msg);
        }
    }

    /**
     * 首字母转小写
     *
     * @param(参数): str
     */
    public static String toLowerCaseFirstOne(String str) {
        if (Character.isLowerCase(str.charAt(0))) {
            return str;
        } else {
            return Character.toLowerCase(str.charAt(0)) + str.substring(1);
        }
    }

    /**
     * 首字母转大写
     *
     * @param(参数): str
     */
    public static String toUpperCaseFirstOne(String str) {
        if (Character.isUpperCase(str.charAt(0))) {
            return str;
        } else {
            return Character.toUpperCase(str.charAt(0)) + str.substring(1);
        }
    }

    /**
     * 通过反射调用对字符串字段进行trim操作
     *
     * @param object
     * @param fieldNames
     */
    static public void trimStringField(Object object, String... fieldNames) {
        if (object == null) {
            return;
        }
        for (String fieldName : fieldNames) {
            if (FieldUtils.isFieldExist(object, fieldName)) {
                try {
                    Field field = FieldUtils.getField(object, fieldName);
                    if (field != null) {
                        field.setAccessible(true);
                        Object oldValue = field.get(object);
                        if (!HlpUtils.isEmpty(oldValue) && oldValue instanceof String) {
                            field.set(object, ((String) oldValue).trim());
                        }
                    }
                } catch (Exception ignored) {
                }
            }
        }
    }

    /**
     * 复制日期范围参数，从前端获取日期数组，转为2个日期参数
     *
     * @param map
     * @param params
     * @param srcName
     * @param beginName 开始日期名称
     * @param endName   结束日期名称（比实际加多1天）
     */
    static public void copyDateRangeParams(Map<String, Object> map, Map<String, Object> params, String srcName, String beginName, String endName, boolean removeSrc) throws ParseException {
        if (map.containsKey(srcName)) {
            List<String> list = HlpUtils.getStringList(map, srcName);
            if (HlpUtils.isEmptyList(list)) {
                return;
            }
            if (list.size() > 1) {
                params.put(beginName, HlpUtils.string2Date(list.get(0), HlpUtils.Format_Yyyy_MM_dd));
                params.put(endName, HlpUtils.getNextDate(HlpUtils.string2Date(list.get(1), HlpUtils.Format_Yyyy_MM_dd), 1));
            }
            if (removeSrc) {
                map.remove(srcName);
            }
        }
    }

    /**
     * 将字符串转列表，如果不是列表的数据会进行提示
     *
     * @param jsonStr
     * @param clazz
     * @param fieldName
     * @param <T>
     * @return
     * @throws HlpException
     */
    public static <T> List<T> parseArray(String jsonStr, Class<T> clazz, String fieldName) throws HlpException {
        if (jsonStr.startsWith("[")) {
            return JSON.parseArray(jsonStr, clazz);
        } else {
            throw new HlpException(fieldName + "不是数组");
        }
    }
    /**
     * 获取字符串的拼音首字母
     * @param name
     * @return
     */
    public static String getPyinFirstLetter(String name){
        if (!HlpUtils.isEmpty(name)){
            return PinyinUtil.getFirstLetter(name,"");
        }
        return null;
    }

    private final static String KEY_NAME_SORT_LIST = "sort_list";
    private final static String ORDER_VALUE_ASC = "asc";
    private final static String ORDER_VALUE_DESC = "desc";
    private final static String SQL_CLAUSE_ORDER_BY_KEY = "order_by";
    private final static String SQL_CLAUSE_BLANK = " ";
    private final static String SQL_CLAUSE_COMMAS = ",";

    /**
     * 检查网络接收的参数map是否存在参数sort_list，有泽生成sql的排序子句（可防止SQL注入），加入到service从参数map中，以便sql执行时排序
     * sql一般写法
     *         <if test="params.order_by != null">
     *         order by ${params.order_by}
     *         </if>
     *         <if test="params.order_by == null">
     *             order by a.id desc
     *         </if>
     * 前端写法：在vxetable的列上加属性：sortable:true
     *  tsd项目的vxetable已设置了支持远程多个字段排序，点击标题的列名进行排序
     * controller的写法：
     *  HlpUtils.genSqlOrderBy(map,params,",code,name,category_name,search_code,short_name,");
     * @param map  网络接收的参数map
     * @param params  提交给service的参数map
     * @param columnsWithComma  允许排序的列名列表，逗号分割，前后都需要有逗号
     */
    public static void genSqlOrderBy(Map<String, Object> map, Map<String, Object> params, String columnsWithComma) {
        Object obj = map.get(KEY_NAME_SORT_LIST);
        StringBuilder bu = new StringBuilder();
        if (obj != null) {
            List<OrderByVo> list = JSONArray.parseArray(JSON.toJSONString(obj), OrderByVo.class);
            for (OrderByVo vo:list){
                if (columnsWithComma.contains(","+vo.getField()+",")){
                    //如果是预设的排序字段，则处理加入
                    //如果中文排序需要按照中文拼音排序，则可以采用convert(column_name using gbk)方式
                    if (bu.length()>0){
                        bu.append(SQL_CLAUSE_COMMAS);
                    }
                    bu.append(SQL_CLAUSE_BLANK);
                    bu.append(vo.getField());
                    if (ORDER_VALUE_DESC.equals(vo.getOrder())){
                        bu.append(SQL_CLAUSE_BLANK);
                        bu.append(ORDER_VALUE_DESC);
                    }
                }
            }
        }
        if (bu.length()>0){
            params.put(SQL_CLAUSE_ORDER_BY_KEY,bu.toString());
        }
    }
}
